/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.knowledge;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.constant.knowledge.EvaluationMethod;
import cn.ac.nercis.pes.common.constant.knowledge.TargetLevel;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.model.dto.knowledge.KmEvaluationMethodDTO;
import cn.ac.nercis.pes.repository.dal.knowledge.KmEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.knowledge.KmTargetRepository;
import cn.ac.nercis.pes.repository.mapper.knowledge.KmEvaluationMethodMapper;
import cn.ac.nercis.pes.repository.model.knowledge.KmEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.knowledge.KmTargetDO;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 测评方法（知识库）服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record KmEvaluationMethodService(KmEvaluationMethodRepository kmEvaluationMethodRepository,
                                        KmTargetRepository kmTargetRepository) {
    @Autowired
    public KmEvaluationMethodService {
    }

    public List<KmEvaluationMethodDTO> findKmEvaluationMethod(String layerTargetName, TargetLevel level) {
        var targets = this.kmTargetRepository.findAllByLevel(level);
        var layerTarget = targets.stream().filter(kt -> StringUtils.equals(kt.getName(), layerTargetName))
                .findFirst()
                .orElse(null);
        if (Objects.isNull(layerTarget)) {
            return Collections.emptyList();
        }
        return targets.stream()
                .filter(ut -> StringUtils.equals(ut.getParentId(), layerTarget.getId()))
                .sorted(Comparator.comparingInt(KmTargetDO::getSort))
                .flatMap(ut -> findKmEvaluationMethod(ut.getId()).stream())
                .toList();
    }

    public Result<String> saveKmEvaluationMethod(KmEvaluationMethodDTO kmEvaluationMethod) {
        try {
            if (StringUtils.isEmpty(kmEvaluationMethod.getId())) {
                return create(kmEvaluationMethod);
            } else {
                return modify(kmEvaluationMethod);
            }
        } catch (Exception e) {
            log.error("保存测评方法（知识库）信息出错：", e);
            return Result.failed();
        }
    }

    public Result<String> batchKmEvaluationMethod(List<KmEvaluationMethodDTO> data) {
        Result<String> result = Result.success();
        for (var item : data) {
            result = saveKmEvaluationMethod(item);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    public Result<String> deleteKmEvaluationMethod(String id) {
        try {
            return this.kmEvaluationMethodRepository.findById(id)
                    .map(entity -> {
                        kmEvaluationMethodRepository.delete(entity);
                        return entity;
                    })
                    .map(KmEvaluationMethodDO::getId)
                    .map(Result::success)
                    .orElse(Result.failed());
        } catch (Exception ex) {
            log.error("删除知识库测评方式信息出错：", ex);
            return Result.failed();
        }
    }

    public Result<String> batchDeleteKmEvaluationMethod(Set<String> ids) {
        Result<String> result = Result.success();
        for (String id : ids) {
            result = deleteKmEvaluationMethod(id);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private Result<String> create(KmEvaluationMethodDTO evaluationMethod) {
        var unitTarget = this.kmTargetRepository.findById(evaluationMethod.getUnitTargetId()).orElse(null);
        if (Objects.isNull(unitTarget)) {
            return Result.failed("未获取到指定测评指标信息！");
        }
        var lastSort = this.kmEvaluationMethodRepository.findAllByUnitTargetId(evaluationMethod.getUnitTargetId())
                .stream()
                .mapToInt(KmEvaluationMethodDO::getSort)
                .max()
                .orElse(0);
        return Optional.of(KmEvaluationMethodMapper.INSTANCE.toDO(evaluationMethod))
                .map(e -> {
                    e.setLevel(unitTarget.getLevel());
                    e.setSort(lastSort + 1);
                    e.setCreateTime(LocalDateTime.now());
                    return e;
                })
                .map(kmEvaluationMethodRepository::save)
                .map(KmEvaluationMethodDO::getId)
                .map(Result::success)
                .orElse(Result.failed());
    }

    private Result<String> modify(KmEvaluationMethodDTO evaluationMethod) {
        var source = this.kmEvaluationMethodRepository.findById(evaluationMethod.getId()).orElse(null);
        if (Objects.isNull(source)) {
            return Result.failed(ResultCode.NOT_FOUND);
        }
        if (!StringUtils.equals(source.getUnitTargetId(), evaluationMethod.getUnitTargetId())) {
            var unitTarget = this.kmTargetRepository.findById(evaluationMethod.getUnitTargetId()).orElse(null);
            if (Objects.isNull(unitTarget)) {
                return Result.failed("未获取到指定测评指标信息！");
            }
            var lastSort = this.kmEvaluationMethodRepository.findAllByUnitTargetId(evaluationMethod.getUnitTargetId())
                    .stream()
                    .mapToInt(KmEvaluationMethodDO::getSort)
                    .max()
                    .orElse(0);
            source.setUnitTargetId(evaluationMethod.getUnitTargetId());
            source.setSort(lastSort + 1);
            source.setLevel(unitTarget.getLevel());
        }
        source.setContent(evaluationMethod.getContent());
        source.setMethod(evaluationMethod.getMethod());
        source.setModifyTime(LocalDateTime.now());
        return Optional.of(kmEvaluationMethodRepository.save(source))
                .map(KmEvaluationMethodDO::getId)
                .map(Result::success)
                .orElse(Result.failed(ResultCode.FAIL));
    }


    private List<KmEvaluationMethodDTO> findKmEvaluationMethod(String unitTargetId) {
        return kmEvaluationMethodRepository.findAllByUnitTargetId(unitTargetId)
                .stream()
                .sorted(Comparator.comparingInt(KmEvaluationMethodDO::getSort))
                .map(KmEvaluationMethodMapper.INSTANCE::toDTO)
                .toList();
    }

    public String getEvalMethodDesc(@NonNull String layerTargetId, List<EvaluationMethod> methods) {
        if (CollectionUtils.isEmpty(methods)) {
            return "";
        }
        return kmTargetRepository.findAllByParentId(layerTargetId)
                .stream()
                .sorted(Comparator.comparingInt(KmTargetDO::getSort))
                .map(ut -> getEvalMethodDesc(ut, methods))
                .findFirst().orElse("");
    }

    private String getEvalMethodDesc(KmTargetDO unitTarget, List<EvaluationMethod> methods) {
        return this.kmEvaluationMethodRepository.findAllByUnitTargetId(unitTarget.getId())
                .stream()
                .filter(em -> methods.stream().anyMatch(m -> StringUtils.contains(em.getMethod(), m.getValue())))
                .sorted(Comparator.comparingInt(KmEvaluationMethodDO::getSort))
                .map(KmEvaluationMethodDO::getContent)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.joining(BaseConstants.NEW_LINE));
    }

}